/*!
 * @file        apm32f4xx_dac.c
 *
 * @brief       This file contains all the functions prototypes for the DAC firmware library
 *
 * @version     V1.0.2
 *
 * @date        2022-06-23
 *
 * @attention
 *
 *  Copyright (C) 2021-2022 Geehy Semiconductor
 *
 *  You may not use this file except in compliance with the
 *  GEEHY COPYRIGHT NOTICE (GEEHY SOFTWARE PACKAGE LICENSE).
 *
 *  The program is only for reference, which is distributed in the hope
 *  that it will be usefull and instructional for customers to develop
 *  their software. Unless required by applicable law or agreed to in
 *  writing, the program is distributed on an "AS IS" BASIS, WITHOUT
 *  ANY WARRANTY OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the GEEHY SOFTWARE PACKAGE LICENSE for the governing permissions
 *  and limitations under the License.
 */

#include "apm32f4xx_dac.h"
#include "apm32f4xx_rcm.h"

/** @addtogroup APM32F4xx_StdPeriphDriver
  @{
*/

/** @defgroup DAC_Driver
  * @brief DAC driver modules
  @{
*/

/** @defgroup DAC_Functions
  @{
*/

/*!
 * @brief     Reset dac peripheral registers to their default reset values.
 *
 * @param     None
 *
 * @retval    None
 */
void DAC_Reset(void)
{
    RCM_EnableAPB1PeriphReset(RCM_APB1_PERIPH_DAC);
    RCM_DisableAPB1PeriphReset(RCM_APB1_PERIPH_DAC);
}

/*!
 * @brief     Config the DAC peripheral according to the specified parameters in the dacConfig
 *
 * @param     channel: Select the DAC channel.
 *                        This parameter can be one of the following values:
 *                        @arg DAC_CHANNEL_1 : DAC channel 1
 *                        @arg DAC_CHANNEL_2 : DAC channel 2
 *
 * @param     dacConfig: pointer to a DAC_Config_T structure
 *
 * @retval    None
 */
void DAC_Config(uint32_t channel, DAC_Config_T* dacConfig)
{
    uint32_t temp1 = 0, temp2 = 0;

    temp1 = DAC->CTRL;

    temp1 &= ~(((uint32_t)0x00000FFE) << channel);

    temp2 = ((uint32_t)dacConfig->trigger | \
             (uint32_t)dacConfig->waveGeneration | \
             (uint32_t)dacConfig->maskAmplitudeSelect | \
             (uint32_t)dacConfig->outputBuffer);

    temp1 |= temp2 << channel;

    DAC->CTRL = temp1;
}

/*!
 * @brief     Fills each DAC_Config_T member with its default value
 *
 * @param     dacConfig: pointer to a DAC_Config_T structure which will be initialized
 *
 * @retval    None
 */
void DAC_ConfigStructInit(DAC_Config_T* dacConfig)
{
    dacConfig->trigger = DAC_TRIGGER_NONE;
    dacConfig->waveGeneration = DAC_WAVE_GENERATION_NONE;
    dacConfig->maskAmplitudeSelect = DAC_LFSR_MASK_BIT11_1;
    dacConfig->outputBuffer = DAC_OUTPUT_BUFFER_ENBALE;
}

/*!
 * @brief     Enables the specified DAC peripheral
 *
 * @param     channel: Select the DAC channel.
 *                     This parameter can be one of the following values:
 *                        @arg DAC_CHANNEL_1 : DAC channel 1
 *                        @arg DAC_CHANNEL_2 : DAC channel 2
 *
 * @retval    None
 */
void DAC_Enable(DAC_CHANNEL_T channel)
{
    if (channel == DAC_CHANNEL_1)
    {
        DAC->CTRL_B.ENCH1 = BIT_SET;
    }
    else if (channel == DAC_CHANNEL_2)
    {
        DAC->CTRL_B.ENCH2 = BIT_SET;
    }
}

/*!
 * @brief     Disables the specified DAC peripheral
 *
 * @param     channel: Select the DAC channel.
 *                     This parameter can be one of the following values:
 *                        @arg DAC_CHANNEL_1 : DAC channel 1
 *                        @arg DAC_CHANNEL_2 : DAC channel 2
 *
 * @retval    None
 */
void DAC_Disable(DAC_CHANNEL_T channel)
{
    if (channel == DAC_CHANNEL_1)
    {
        DAC->CTRL_B.ENCH1 = BIT_RESET;
    }
    else if (channel == DAC_CHANNEL_2)
    {
        DAC->CTRL_B.ENCH2 = BIT_RESET;
    }
}

/*!
 * @brief     Enables the specified DAC channel DMA request
 *
 * @param     channel: Select the DAC channel.
 *                        This parameter can be one of the following values:
 *                        @arg DAC_CHANNEL_1 : DAC channel 1
 *                        @arg DAC_CHANNEL_2 : DAC channel 2
 *
 * @retval    None
 */
void DAC_DMA_Enable(DAC_CHANNEL_T channel)
{
    if (channel == DAC_CHANNEL_1)
    {
        DAC->CTRL_B.DMAENCH1 = BIT_SET;
    }
    else if (channel == DAC_CHANNEL_2)
    {
        DAC->CTRL_B.DMAENCH2 = BIT_SET;
    }
}

/*!
 * @brief     Disables the specified DAC channel DMA request
 *
 * @param     channel: Select the DAC channel.
 *                     This parameter can be one of the following values:
 *                        @arg DAC_CHANNEL_1 : DAC channel 1
 *                        @arg DAC_CHANNEL_2 : DAC channel 2
 *
 * @retval    None
 */
void DAC_DMA_Disable(DAC_CHANNEL_T channel)
{
    if (channel == DAC_CHANNEL_1)
    {
        DAC->CTRL_B.DMAENCH1 = BIT_RESET;
    }
    else if (channel == DAC_CHANNEL_2)
    {
        DAC->CTRL_B.DMAENCH2 = BIT_RESET;
    }
}

/*!
 * @brief     Enables the selected DAC channel software trigger
 *
 * @param     channel: Select the DAC channel.
 *                     This parameter can be one of the following values:
 *                        @arg DAC_CHANNEL_1 : DAC channel 1
 *                        @arg DAC_CHANNEL_2 : DAC channel 2
 *
 * @retval    None
 */
void DAC_EnableSoftwareTrigger(DAC_CHANNEL_T channel)
{
    if (channel == DAC_CHANNEL_1)
    {
        DAC->SWTRG_B.SWTRG1 = BIT_SET;
    }
    else if (channel == DAC_CHANNEL_2)
    {
        DAC->SWTRG_B.SWTRG2 = BIT_SET;
    }
}

/*!
 * @brief     Disable the selected DAC channel software trigger
 *
 * @param     channel: Select the DAC channel.
 *                     This parameter can be one of the following values:
 *                        @arg DAC_CHANNEL_1 : DAC channel 1
 *                        @arg DAC_CHANNEL_2 : DAC channel 2
 *
 * @retval    None
 */
void DAC_DisableSoftwareTrigger(DAC_CHANNEL_T channel)
{
    if (channel == DAC_CHANNEL_1)
    {
        DAC->SWTRG_B.SWTRG1 = BIT_RESET;
    }
    else if (channel == DAC_CHANNEL_2)
    {
        DAC->SWTRG_B.SWTRG2 = BIT_RESET;
    }
}

/*!
 * @brief     Enables simultaneously the two DAC channels software
 *
 * @param     None
 *
 * @retval    None
 */
void DAC_EnableDualSoftwareTrigger(void)
{
    DAC->SWTRG_B.SWTRG1 = BIT_SET;
    DAC->SWTRG_B.SWTRG2 = BIT_SET;
}

/*!
 * @brief     Disables simultaneously the two DAC channels software
 *
 * @param     None
 *
 * @retval    None
 */
void DAC_DisableDualSoftwareTrigger(void)
{
    DAC->SWTRG_B.SWTRG1 = BIT_RESET;
    DAC->SWTRG_B.SWTRG2 = BIT_RESET;
}

/*!
 * @brief     Enables the selected DAC channel wave generation
 *
 * @param     channel: Select the DAC channel.
 *                     This parameter can be one of the following values:
 *                     @arg DAC_CHANNEL_1 : DAC channel 1
 *                     @arg DAC_CHANNEL_2 : DAC channel 2
 *
 * @param     wave: Select the wave
 *                     This parameter can be one of the following values:
 *                     @arg DAC_WAVE_GENERATION_NOISE    : Noise wave generation
 *                     @arg DAC_WAVE_GENERATION_TRIANGLE : Triangle wave generation
 *
 * @retval    None
 */
void DAC_EnableWaveGeneration(DAC_CHANNEL_T channel, DAC_WAVE_GENERATION_T wave)
{
    DAC->CTRL &= 0xFF3FFF3F;
    DAC->CTRL |= wave << channel;
}

/*!
 * @brief     Disables the selected DAC channel wave generation
 *
 * @param     channel: Select the DAC channel.
 *                     This parameter can be one of the following values:
 *                     @arg DAC_CHANNEL_1 : DAC channel 1
 *                     @arg DAC_CHANNEL_2 : DAC channel 2
 *
 * @param     wave: Select the wave
 *                     This parameter can be one of the following values:
 *                     @arg DAC_WAVE_GENERATION_NOISE    : Noise wave generation
 *                     @arg DAC_WAVE_GENERATION_TRIANGLE : Triangle wave generation
 *
 * @retval    None
 */
void DAC_DisableWaveGeneration(DAC_CHANNEL_T channel, DAC_WAVE_GENERATION_T wave)
{
    DAC->CTRL &= ~(wave << channel);
}

/*!
 * @brief     Set the specified data holding register value for DAC channel 1
 *
 * @param     align: DAC channel 1 data alignment
 *                   This parameter can be one of the following values:
 *                      @arg DAC_ALIGN_12BIT_R : 12-bit right-aligned data
 *                      @arg DAC_ALIGN_12BIT_L : 12-bit left-aligned data
 *                      @arg DAC_ALIGN_8BIT_R  : 8-bit right-aligned data
 *
 * @param     data: The data to be loaded in the selected data register.
 *
 * @retval    None
 */
void DAC_ConfigChannel1Data(DAC_ALIGN_T align, uint16_t data)
{
    __IO uint32_t temp = 0;

    temp = (uint32_t)DAC_BASE;
    temp += 0x00000008 + align;

    /* Set the DAC channel1 selected data holding register */
    *(__IO uint32_t *) temp = data;
}

/*!
 * @brief     Set the specified data holding register value for DAC channel 2
 *
 * @param     align: DAC channel 2 data alignment
 *                      This parameter can be one of the following values:
 *                      @arg DAC_ALIGN_12BIT_R : 12-bit right-aligned data
 *                      @arg DAC_ALIGN_12BIT_L : 12-bit left-aligned data
 *                      @arg DAC_ALIGN_8BIT_R  : 8-bit right-aligned data
 *
 * @param     data: The data to be loaded in the selected data register.
 *
 * @retval    None
 */
void DAC_ConfigChannel2Data(DAC_ALIGN_T align, uint16_t data)
{
    __IO uint32_t temp = 0;

    temp = (uint32_t)DAC_BASE;
    temp += 0x00000014 + align;

    /* Set the DAC channel1 selected data holding register */
    *(__IO uint32_t *) temp = data;
}

/*!
 * @brief     Set the specified data holding register value for dual DAC channel
 *
 * @param     align: Dual DAC channel data alignment
 *                   This parameter can be one of the following values:
 *                      @arg DAC_ALIGN_12BIT_R : 12-bit right-aligned data
 *                      @arg DAC_ALIGN_12BIT_L : 12-bit left-aligned data
 *                      @arg DAC_ALIGN_8BIT_R  : 8-bit right-aligned data
 *
 * @param     data2: Data for channel2 to be loaded in the selected data register.
 *
 * @param     data1: Data for channel1 to be loaded in the selected data register.
 *
 * @retval    None
 */
void DAC_ConfigDualChannelData(DAC_ALIGN_T align, uint16_t data2, uint16_t data1)
{
    uint32_t data = 0, temp = 0;

    /* Calculate and set dual DAC data holding register value */
    if (align == DAC_ALIGN_8BIT_R)
    {
        data = ((uint32_t)data2 << 8) | data1;
    }
    else
    {
        data = ((uint32_t)data2 << 16) | data1;
    }

    temp = (uint32_t)DAC_BASE;
    temp += 0x00000020 + align;

    /* Set the dual DAC selected data holding register */
    *(__IO uint32_t *)temp = data;
}

/*!
 * @brief     Reads the specified DAC channel data output value.
 *
 * @param     channel: Select the DAC channel.
 *                     This parameter can be one of the following values:
 *                       @arg DAC_CHANNEL_1 : DAC channel 1
 *                       @arg DAC_CHANNEL_2 : DAC channel 2
 *
 * @retval    The data output value of the specified DAC channel.
 */
uint16_t DAC_ReadDataOutputValue(DAC_CHANNEL_T channel)
{
    __IO uint32_t temp = 0;

    temp = (uint32_t) DAC_BASE ;
    temp += 0x0000002C + ((uint32_t)channel >> 2);

    /* Returns the DAC channel data output register value */
    return (uint16_t) (*(__IO uint32_t*) temp);
}

/*!
 * @brief     Enable the specified DAC underrun interrupt.
 *
 * @param     channel: The selected DAC channel.
 *                        This parameter can be one of the following values:
 *                        @arg DAC_CHANNEL_1 : DAC channel 1
 *                        @arg DAC_CHANNEL_2 : DAC channel 2
 *
 * @retval    None
 */
void DAC_EnableInterrupt(DAC_CHANNEL_T channel)
{
    if (channel == DAC_CHANNEL_1)
    {
        DAC->CTRL_B.DMAUDIEN1 = BIT_SET;
    }
    else if (channel == DAC_CHANNEL_2)
    {
        DAC->CTRL_B.DMAUDIEN2 = BIT_SET;
    }
}

/*!
 * @brief     Disable the specified DAC underrun interrupt.
 *
 * @param     channel: The selected DAC channel.
 *                     This parameter can be one of the following values:
 *                        @arg DAC_CHANNEL_1 : DAC channel 1
 *                        @arg DAC_CHANNEL_2 : DAC channel 2
 *
 * @retval    None
 */
void DAC_DisableInterrupt(DAC_CHANNEL_T channel)
{
    if (channel == DAC_CHANNEL_1)
    {
        DAC->CTRL_B.DMAUDIEN1 = BIT_RESET;
    }
    else if (channel == DAC_CHANNEL_2)
    {
        DAC->CTRL_B.DMAUDIEN2 = BIT_RESET;
    }
}

/*!
 * @brief     Reads the specified DAC underrun flag
 *
 * @param     channel: The selected DAC channel.
 *                        This parameter can be one of the following values:
 *                        @arg DAC_CHANNEL_1 : DAC channel 1
 *                        @arg DAC_CHANNEL_2 : DAC channel 2
 *
 * @retval    The new state of DAC_FLAG (SET or RESET).
 */
uint8_t DAC_ReadStatusFlag(DAC_CHANNEL_T channel)
{
    if (channel == DAC_CHANNEL_1)
    {
        return  DAC->STS_B.DMAUDFLG1;
    }
    else
    {
        return  DAC->STS_B.DMAUDFLG2;
    }
}

/*!
 * @brief     Clears the DAC channel's status flags.
 *
 * @param     channel: The selected DAC channel.
 *                     This parameter can be one of the following values:
 *                        @arg DAC_Channel_1: DAC Channel1 selected
 *                        @arg DAC_Channel_2: DAC Channel2 selected
 *
 * @retval    None
 */
void DAC_ClearStatusFlag(DAC_CHANNEL_T channel)
{
    if (channel == DAC_CHANNEL_1)
    {
        DAC->STS_B.DMAUDFLG1 = SET;
    }
    else
    {
        DAC->STS_B.DMAUDFLG2 = SET;
    }
}

/*!
 * @brief     Reads the specified DAC Interrupt flag.
 *
 * @param     DAC_Channel: The selected DAC channel.
 *                         This parameter can be one of the following values:
 *                            @arg DAC_CHANNEL_1 : DAC channel 1
 *                            @arg DAC_CHANNEL_2 : DAC channel 2
 *
 * @retval    The new state of DAC_IT (SET or RESET).
 */
uint8_t DAC_ReadIntFlag(DAC_CHANNEL_T channel)
{
    if (channel == DAC_CHANNEL_1)
    {
        return (DAC->CTRL_B.DMAUDIEN1 && DAC->STS_B.DMAUDFLG1);
    }
    else
    {
        return (DAC->CTRL_B.DMAUDIEN2 && DAC->STS_B.DMAUDFLG2);
    }
}

/*!
 * @brief     Clears the DAC channel's interrupt flag.
 *
 * @param     channel: The selected DAC channel.
 *                     This parameter can be one of the following values:
 *                  @arg DAC_CHANNEL_1 : DAC channel 1
 *                  @arg DAC_CHANNEL_2 : DAC channel 2
 *
 * @retval    None
 */
void DAC_ClearIntFlag(DAC_CHANNEL_T channel)
{
    if (channel == DAC_CHANNEL_1)
    {
        DAC->STS_B.DMAUDFLG1 = SET;
    }
    else
    {
        DAC->STS_B.DMAUDFLG2 = SET;
    }
}

/**@} end of group DAC_Functions */
/**@} end of group DAC_Driver */
/**@} end of group APM32F4xx_StdPeriphDriver */
